package com.haipumi.dao;

import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DBHelper {

	//这里取到的是tomcat提供的联接
	//获取与数据库的连接
	public Connection getCon(){
		Connection con=null;//获取tomcat提供的数据库连接
		try {
			Context cx=new InitialContext();//初始化tomcat容器环境的上下文
			/*Context envCtx=(Context)cx.lookup("java:comp/env");//查找java资源的命名
			DataSource ds=(DataSource)envCtx.lookup("jdbc/res");//查找名字为res的资源,即tomcat提供的数据源
			*/
			DataSource ds=(DataSource)cx.lookup("java:comp/env/jdbc/haipumi");//查找java资源的命名和查找名字为res的资源
			con=ds.getConnection();
		} catch (NamingException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return con;
	}
	
	//查询数据
	public List<Map<String,String>> findAll(String sql,List<Object>params) throws SQLException, IOException, NamingException{
		Connection con=getCon();
		PreparedStatement pstmt=con.prepareStatement(sql);
		setParams(pstmt,params);
		// 得到结果集
		ResultSet rs=pstmt.executeQuery();
		//取出所有的列名，存到list中
		//从结果集中取出列名
		ResultSetMetaData rsmd=rs.getMetaData();
		//取出列数
		int columnCount=rsmd.getColumnCount();
		List<String>cnlist=new ArrayList<String>();
		for(int i=1;i<=columnCount;i++){
			cnlist.add(rsmd.getColumnName(i));
		}
		List<Map<String,String>>list=new ArrayList<Map<String,String>>();
		//取出表中的数据
		while(rs.next()){
			Map<String,String>map=new HashMap<String,String>();
			//循环cnlist中所有的列名，再根据这个列名从rs 中取数据
			for(String cn:cnlist){
				map.put(cn, rs.getString(cn));
			}
			list.add(map);
		}
		closeAll(pstmt,con,rs);
		return list;
	}
	
	
	//查询聚合函数的方法： count  avg  max  min
	public double findDouble(String sql,List<Object>params) throws SQLException{
		double result=0;
		Connection con=getCon();
		PreparedStatement pstmt=con.prepareStatement(sql);
		setParams(pstmt,params);
		//得到结果集
		ResultSet rs=pstmt.executeQuery();
		if(rs.next()){
			result=rs.getDouble(1);
		}
		closeAll(pstmt,con,rs);
		return result;
	}
	
	
	//封装增删改
	public int doUpdate(String sql,List<Object>params) throws SQLException{
		Connection con=getCon();
		PreparedStatement pstmt=con.prepareStatement(sql);
		setParams(pstmt,params);
		int result=pstmt.executeUpdate();
		closeAll(pstmt,con,null);
		return result;
	}
	//封装事务的增删改
	public void doUpdate(List<String> sqls,List<List<Object>>listparams) throws SQLException, IOException, NamingException{
		Connection con=getCon();
		PreparedStatement pstmt=null;
		//关闭隐式事务提交（一句sql语句提交一次）
		con.setAutoCommit(false);
		try{
			if(sqls!=null&&sqls.size()>0){
				for(int i =0 ; i<sqls.size();i++){
					String sql=sqls.get(i);//取出每条sql 语句
					pstmt=con.prepareStatement(sql);
					setParams(pstmt,listparams.get(i));
					pstmt.executeUpdate();
				}
			}
			con.commit();//提交事务
		}catch(Exception e){
			if(con!=null){
				con.rollback();
			}
		}finally{
			con.setAutoCommit(true);//再次恢复事务为隐式事务
			closeAll(pstmt,con,null);
		}
	}
	
	
	//设置sql语句当中的参数，如果有的话
	private void setParams(PreparedStatement pstmt, List<Object> params) throws SQLException {
		if(params!=null&& params.size()>0){
			for(int i=1;i<=params.size();i++){
				pstmt.setObject(i, params.get(i-1));
			}
		}
		
	}
	//关闭连接
	private void closeAll(PreparedStatement pstmt,Connection con,ResultSet rs) throws SQLException{
		if(rs!=null){
			rs.close();
		}
		if(pstmt!=null){
			pstmt.close();
		}
		if(con!=null){
			con.close();
		}
	}

	public List<Map<String, String>> find(String sql, List<Object> params) throws SQLException, IOException, NamingException {
		List<Map<String,String>> list=new ArrayList<Map<String,String>>();
		Connection con=getCon();
		PreparedStatement pstmt=con.prepareStatement(sql);
		setParams(pstmt,params);
		ResultSet rs=pstmt.executeQuery();
		
		List<String> cnames=new ArrayList<String>();
		ResultSetMetaData rsmd=rs.getMetaData();
		for(int i=0;i<rsmd.getColumnCount();i++){
			cnames.add(rsmd.getColumnName(i+1));
		}
		
		while(rs.next()){
			Map<String,String>map =new HashMap<String,String>();
			for(String cn:cnames){
				String value=rs.getString(cn);
				map.put(cn, value);
			}
			list.add(map);
		}
		closeAll(pstmt,con,rs);
		return list;
	}
	
	/**
	 * 	基于对象的查询
	 * @param sql
	 * @param params
	 * @param c
	 * @return
	 * @throws Exception 
	 */
	public <T> List<T> finds(String sql,List<Object> params,Class<T> c) throws Exception{
		List<T> list =new ArrayList<T>();
		Connection con=this.getCon();
		PreparedStatement pstmt=con.prepareStatement(sql);
		setParams(pstmt,params);
		ResultSet rs=pstmt.executeQuery();
		
		//获取返回的结果中所有列的列名
		ResultSetMetaData rsmd=rs.getMetaData();
		String[] colNames=new String[rsmd.getColumnCount()];
		for(int i=0;i<colNames.length;i++){
			colNames[i]=rsmd.getColumnName(i+1);//获取每个列的列名
		}
		//获取给定的类文件中所有的方法
		Method[] ms=c.getMethods();
//		for(Method m:ms){
//			System.out.println(m.getName());
//		}
		T t;
		String methodName=null;	//方法名
		String colName=null;	//列名
		String type=null;	//类型
		Object obj=null;
		while(rs.next()){//每循环一次就是一个对象,所以每循环一次就实例化一个对象
			//通过发射创建一个类的实例
			t=c.newInstance();
			//循环所有的列,取出每个列的值
			for(int i=0;i<colNames.length;i++){
				colName="set"+colNames[i];//setUSID setUsid
				if(ms!=null && ms.length>0){//如果给定的类中有提供方法,则循环比较
					for(Method m:ms){
						methodName=m.getName();
						if(colName.equalsIgnoreCase(methodName)){
			//如果当前循环的列名加上set后与当前的方法名,忽略大小写后相同,则获取类的类型并激活此方法注值
							obj=rs.getObject(colNames[i]);//获取当前列对应的值
							if(obj!=null){
								//如果当前的值不为空,则取出该值的类型
								type=obj.getClass().getName();
								//激活对应的方法赋值
								//TODO 
								if("java.math.BigDecimal".equals(type)){
									try{
										m.invoke(t, rs.getInt(colNames[i]));
									}catch(Exception e){
										m.invoke(t, rs.getDouble(colNames[i]));
									}
								}else if("java.lang.String".equals(type)){
									m.invoke(t, String.valueOf(obj));
								}else{
									m.invoke(t, String.valueOf(obj));
								}
							}else{//如果返回的字段的值为空,则当字符串处理
								m.invoke(t, String.valueOf(obj));
							}
						}
					}	
				}
			}
			list.add(t);
		}
		return list;
	}

	private static <T> T parsetMap(Map<String ,String > map,Class<T> c) throws Exception{
		T t=null;
		try {
			t=c.newInstance();//根据反射实例生成对象
			Method[] ms=c.getMethods();
			//2.循环所有的键,在前面加一个set 形成setxxx
			for(Entry<String, String> entry : map.entrySet() ){
				String key=entry.getKey();
				String v=entry.getValue();
				key="set"+key;//方法名
				//3.循环 反射实例c中所有的方法,查找上面的setxxx的方法
				for(Method m:ms){
					if(key.equalsIgnoreCase(m.getName())){
						//4.激活这个方法,将这个键所对应的值即可,相当于你调用了set 设置了值
						//判断 m的参数类型
						String parameterType=m.getParameterTypes()[0].getName();//取出方法的参数类型名
						if("int".equals(parameterType) || "java.lang.Integer".equals(parameterType)){
							m.invoke(t, Integer.parseInt(v));
						}else if("float".equals(parameterType)|| "java.lang.Float".equals(parameterType)){
							m.invoke(t, Float.parseFloat(v));
						}else if("double".equals(parameterType)|| "java.lang.Double".equals(parameterType)){
							m.invoke(t, Double.parseDouble(v));
						}else if("long".equals(parameterType)|| "java.lang.Long".equals(parameterType)){
							m.invoke(t, Long.parseLong(v));
						}else if( "java.lang.String".equals(parameterType)){
							m.invoke(t, v);
						}
						break;
					}
				}
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
			throw new RuntimeException( e);
		}
		return t;
	}
	
	public <T> List<T> findAll(String sql,List<Object> params,Class<T> t)throws Exception{
		List<Map<String,String>> listmap=findAll(sql,params);
		//将list<Map>转为List<T>
		List<T> list=new ArrayList<T>();
		if(listmap!=null && listmap.size()>0){
			for(Map<String,String> map:listmap){
				T obj=parsetMap(map,t);
				list.add(obj);
			}
		}
		return list;
	}

	/**
	 * 返回主键的插入
	 * @param sql
	 * @param params
	 * @return
	 * @throws SQLException
	 */
	public int insertWithKey(String sql, List<Object> params) throws SQLException {
		Connection con = getCon();
		PreparedStatement pstmt = con.prepareStatement(sql,
				PreparedStatement.RETURN_GENERATED_KEYS);
		setParams(pstmt,params);
		pstmt.executeUpdate();
		int autoIncKeyFromApi = -1;
		ResultSet rs = pstmt.getGeneratedKeys();   //得到主键
		if (rs.next()) {
			autoIncKeyFromApi = rs.getInt(1);
		} else {
			throw new RuntimeException("无法插入新数据,序列号无法生成");
		}
		closeAll(pstmt,con,rs);
		return autoIncKeyFromApi;
	}
	

}
